from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
from sklearn.metrics import accuracy_score
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras import optimizers
from tensorflow.keras import regularizers


def threshold(x, d):  # `硬阈值函数`
    return [1 if xi > d else 0 for xi in x]


def fit(x, y, LearningRate, epoches, w, b):  # `训练函数`
    for step in range(epoches):
        for i in range(x.shape[0]):
            h = threshold(np.dot(w, x[i]) + b, 0)
            w = w + LearningRate * (y[i] - h) * x[i]  # `感知机规则`
            b = b + LearningRate * (y[i] - h)
    return w, b


def predict(x):  # `分类函数`
    return threshold(np.dot(x, w) + b, 0)


# 加载乳腺癌数据集
breast_cancer = datasets.load_breast_cancer()
# 将数据集分割为训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(
    breast_cancer.data,
    breast_cancer.target,
    test_size=0.3,
    random_state=420)
# 初始化权重和偏置
w = np.random.random(x_train.shape[1])  # `w,b随机初始化`
b = np.random.random(1)

# 训练模型
w, b = fit(x_train, y_train, 0.001, 2000, w, b)  # `训练w,b`
# 计算训练集和测试集的准确率
pred_train = predict(x_train)
pred_test = predict(x_test)
print(accuracy_score(y_train, pred_train))
print(accuracy_score(y_test, pred_test))

# `以上是硬阈值线性分类，以下是logistic线性分类`
# 以下是logistic线性分类
model = Sequential()
# 添加隐藏层，激活函数为sigmoid，正则化项为l1正则化，参数为0.2
model.add(Dense(input_dim=x_train.shape[1],
                units=1,
                activation='sigmoid',
                kernel_regularizer=regularizers.l1(0.2)))

# 初始化优化器
op = optimizers.RMSprop(learning_rate=0.0001)
# 编译模型，损失函数为mse
model.compile(loss='mse', optimizer=op)

# 训练模型
for epoch in range(20000):
    cost = model.train_on_batch(x_train, y_train)
    # 每1000次迭代打印一次损失函数值
    if epoch % 1000 == 0:
        print("epoch %d , cost: %f" % (epoch, cost))

# 获取权重和偏置
w, b = model.layers[0].get_weights()
print('Weights=', w, '\nbiases=', b)

# 计算训练集和测试集的准确率
pred_train = threshold(model.predict(x_train), 0.5)
pred_test = threshold(model.predict(x_test), 0.5)
print(accuracy_score(y_train, pred_train))
print(accuracy_score(y_test, pred_test))
